package correctness;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.Map;

import model.LogicType;
import model.editormodel.EditorModel;
import model.editormodel.Input;
import model.editormodel.Output;
import model.simulationmodel.LogicValue;
import model.simulationmodel.SimulationModel;
import model.simulationmodel.logicelements.LogicSwitch;

import org.junit.BeforeClass;
import org.junit.Test;

/**
 * <b> Mit dieser Testklasse wird folgende Vorgabe getestet:</b> <br>
 * <br>
 * Eine Schaltung mit zwei Schaltern, einem FlipFlop, und zwei Lampen. Der Test
 * prueft die Funktion des FlipFlop. <br>
 * Am Anfang liegt an beiden Eingaengen L0 an. Die Lampen muessen grau bleiben.
 * Dann wird "set" auf L1 geschaltet. Die erste Lampe muss eingeschaltet sein,
 * waehrend die zweite aus bleibt. Dann wird "set" auf L0 und "reset" auf L1
 * geschaltet. Die zweite Lampe muss eingeschaltet sein, waehrend die erste aus
 * bleibt.
 * 
 * @author Team Locsi
 * 
 */
public class FlipFlopTest {

	private static SimulationModel sModel;

	@BeforeClass
	public static void initialize() {

		// Initialisieren einer neuen Schaltung
		EditorModel eModel = new EditorModel();

		// Erzeugen der benoetigten logischen Elemente
		eModel.addLogicElement(LogicType.SWITCH, "SWITCH", 10, 20);
		eModel.addLogicElement(LogicType.SWITCH, "SWITCH", 10, 40);
		eModel.addLogicElement(LogicType.FLIPFLOP, "FLIPFLOP", 20, 20);
		eModel.addLogicElement(LogicType.LAMP, "LAMP", 50, 20);
		eModel.addLogicElement(LogicType.LAMP, "LAMP", 50, 40);

		// Um Verbindungen setzen zu koennen, werden die Inputs und Outputs der
		// einzelnen Bauteile benoetigt. Dazu werden hier die benoetigten Inputs
		// der einzelnen Bauteile gespeichert.
		Map<String, Input> flipflopInputs = new HashMap<String, Input>(eModel
				.getCircuitModel().getUnmodifableElementsOnEditor()
				.get("FLIPFLOP_3").getInputs());
		Map<String, Input> lamp4Inputs = new HashMap<String, Input>(eModel
				.getCircuitModel().getUnmodifableElementsOnEditor()
				.get("LAMP_4").getInputs());
		Map<String, Input> lamp5Inputs = new HashMap<String, Input>(eModel
				.getCircuitModel().getUnmodifableElementsOnEditor()
				.get("LAMP_5").getInputs());

		// Hier werden die benoetigten Outputs der einzelnen Bauteile
		// gespeichert
		Map<String, Output> switch1Outputs = new HashMap<String, Output>(eModel
				.getCircuitModel().getUnmodifableElementsOnEditor()
				.get("SWITCH_1").getOutputs());
		Map<String, Output> switch2Outputs = new HashMap<String, Output>(eModel
				.getCircuitModel().getUnmodifableElementsOnEditor()
				.get("SWITCH_2").getOutputs());
		Map<String, Output> flipflopOutputs = new HashMap<String, Output>(
				eModel.getCircuitModel().getUnmodifableElementsOnEditor()
						.get("FLIPFLOP_3").getOutputs());

		// Setzen der Verbindungen zwischen den beiden Schaltern und dem
		// FlipFlop
		eModel.setConnection(switch1Outputs.get(Output.STD_O),
				flipflopInputs.get(Input.FF_SET));
		eModel.setConnection(switch2Outputs.get(Output.STD_O),
				flipflopInputs.get(Input.FF_RESET));

		// Setzen der Verbindungen zwischen dem FlipFlop und den beiden Lampen
		eModel.setConnection(flipflopOutputs.get(Output.FF_Q),
				lamp4Inputs.get(Input.STD_A));
		eModel.setConnection(flipflopOutputs.get(Output.FF_NQ),
				lamp5Inputs.get(Input.STD_A));

		// Erzeugen des SimulationModels
		sModel = new SimulationModel(eModel);

		// Updaten des SimulationModels
		sModel.updateSimulationElements();
	}

	@Test
	public void testFlipFlopSimulation() {

		// Setzen der logischen Werte beider Schalter auf L0
		sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("SWITCH_1"))
				.setInputValue(Input.STD_A, LogicValue.L0);

		sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("SWITCH_2"))
				.setInputValue(Input.STD_A, LogicValue.L0);

		// Der logische Ausgangswert der beiden Schalter wird berechnet und
		// anschliessend werden die nachfolgenden Bauteile berechnet
		((LogicSwitch) sModel.getSimulationElements().get(
				sModel.getLogicElements().get("SWITCH_1"))).calcLogicValue();
		((LogicSwitch) sModel.getSimulationElements().get(
				sModel.getLogicElements().get("SWITCH_2"))).calcLogicValue();
		sModel.calcElementsToCalc();

		// Speichert den logischen Wert am Ausgang der ersten Lampe
		LogicValue lamp4Value = sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("LAMP_4"))
				.getLogicValue(Output.STD_O);

		// Speichert den logischen Wert am Ausgang der zweiten Lampe
		LogicValue lamp5Value = sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("LAMP_5"))
				.getLogicValue(Output.STD_O);

		// Prueft, ob der logische Wert am Ausgang der ersten Lampe UNDEFINED
		// ist
		assertEquals(
				"Der Wert am Ausgang der ersten Lampe ist ungleich UNDEFINED.",
				LogicValue.UNDEFINED, lamp4Value);

		// Prueft, ob der logische Wert am Ausgang der zweiten Lampe UNDEFINED
		// ist
		assertEquals(
				"Der Wert am Ausgang der zweiten Lampe ist ungleich UNDEFINED.",
				LogicValue.UNDEFINED, lamp5Value);

		// Setzen des logischen Werts des ersten Schalters (set) auf L1
		sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("SWITCH_1"))
				.setInputValue(Input.STD_A, LogicValue.L1);

		// Der logische Ausgangswert der beiden Schalter wird berechnet und
		// anschliessend werden die nachfolgenden Bauteile berechnet
		((LogicSwitch) sModel.getSimulationElements().get(
				sModel.getLogicElements().get("SWITCH_1"))).calcLogicValue();
		((LogicSwitch) sModel.getSimulationElements().get(
				sModel.getLogicElements().get("SWITCH_2"))).calcLogicValue();
		sModel.calcElementsToCalc();

		// Speichert den logischen Wert am Ausgang der ersten Lampe
		lamp4Value = sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("LAMP_4"))
				.getLogicValue(Output.STD_O);

		// Speichert den logischen Wert am Ausgang der zweiten Lampe
		lamp5Value = sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("LAMP_5"))
				.getLogicValue(Output.STD_O);

		// Prueft, ob der logische Wert am Ausgang der ersten Lampe L1 ist
		assertEquals("Der Wert am Ausgang der ersten Lampe ist ungleich L1.",
				LogicValue.L1, lamp4Value);

		// Prueft, ob der logische Wert am Ausgang der zweiten Lampe L0 ist
		assertEquals("Der Wert am Ausgang der zweiten Lampe ist ungleich L0.",
				LogicValue.L0, lamp5Value);

		// Setzen des logischen Werts des ersten Schalters (set) auf L0
		sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("SWITCH_1"))
				.setInputValue(Input.STD_A, LogicValue.L0);

		// Setzen des logischen Werts des zweiten Schalters (reset) auf L1
		sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("SWITCH_2"))
				.setInputValue(Input.STD_A, LogicValue.L1);

		// Der logische Ausgangswert der beiden Schalter wird berechnet und
		// anschliessend werden die nachfolgenden Bauteile berechnet
		((LogicSwitch) sModel.getSimulationElements().get(
				sModel.getLogicElements().get("SWITCH_1"))).calcLogicValue();
		((LogicSwitch) sModel.getSimulationElements().get(
				sModel.getLogicElements().get("SWITCH_2"))).calcLogicValue();
		sModel.calcElementsToCalc();

		// Speichert den logischen Wert am Ausgang der ersten Lampe
		lamp4Value = sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("LAMP_4"))
				.getLogicValue(Output.STD_O);

		// Speichert den logischen Wert am Ausgang der zweiten Lampe
		lamp5Value = sModel.getSimulationElements()
				.get(sModel.getLogicElements().get("LAMP_5"))
				.getLogicValue(Output.STD_O);

		// Prueft, ob der logische Wert am Ausgang der ersten Lampe L0 ist
		assertEquals("Der Wert am Ausgang der ersten Lampe ist ungleich L0.",
				LogicValue.L0, lamp4Value);

		// Prueft, ob der logische Wert am Ausgang der zweiten Lampe L1 ist
		assertEquals("Der Wert am Ausgang der zweiten Lampe ist ungleich L1.",
				LogicValue.L1, lamp5Value);
	}
}